package com.hup.winHolderService.security;

import com.hup.winHolderService.framework.exception.AppAccessException;
import com.hup.winHolderService.framework.exception.AppMsgException;
import com.hup.winHolderService.framework.exception.AppServerException;
import com.hup.winHolderService.framework.util.net.HttpUtil;
import com.hup.winHolderService.model.LoginReq;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;

/**
 * [登陆,认证]处理类
 *
 * @author hugan
 * @date 2023/10/4
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SecurityService implements WebMvcConfigurer, HandlerInterceptor {

    private static final String HEADER_TOKEN = "tk";
    private static final String HEADER_MOCK = "mock";

    /**
     * 开启模拟用户,开发时使用
     * mock实际生效条件:
     * .enableMock=true
     * .请求HEADER_MOCK=非空
     */
    @Value("${security.enableMock:false}")
    private boolean enableMock;
    /**
     * 打印非正常请求的日志: 请求头缺失,校验不通过等
     */
    @Value("${security.logAbnormal:true}")
    private boolean logAbnormal;

    @Value("${security.pwd}")
    private String pwd;
    @Value("${security.pwdTips}")
    private String pwdTips;

    private final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(this)
                .addPathPatterns("/api/**");
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String uri = request.getRequestURI();
        if (!(handler instanceof HandlerMethod)) {
            if ("OPTIONS".equalsIgnoreCase(request.getMethod())) return true;//跨域
            if (handler instanceof ResourceHttpRequestHandler) return true;//404, 异常的前端资源
            //其他情况,目前没遇到
            log.error("req={}, {}, handler={}", uri, request.getMethod(), handler);
            throw new AppServerException("未知请求:uri=" + uri + ", method=" + request.getMethod() + ", handler=" + handler);
        }
        //log.info("uri={}", uri);

        //认证 校验
        //mock
        if (enableMock && StringUtils.hasText(request.getHeader(HEADER_MOCK))) {
            return true;
        }

        //校验token
        String token = request.getHeader(HEADER_TOKEN);
        if (!StringUtils.hasText(token))
            throw AppAccessException.e401("token为空").setShowLog(logAbnormal);//被盗用
        //对比[明文配置,密文header]
        boolean matches = checkToken(token);
        if (!matches) throw AppAccessException.e401("token验证错误").setShowLog(logAbnormal);//被盗用
        return true;
    }

    //=================

    public boolean checkToken(String token) {
        return passwordEncoder.matches(pwd, token);
    }

    public String login(LoginReq req) {
        boolean matches = Objects.equals(pwd, req.getPwd());
        if (!matches) {
            AppAccessException.e401("认证错误=" + req.getPwd()).setShowLog(true).doLog();
            throw new AppMsgException("认证错误");
        }
        HttpServletRequest request = HttpUtil.getRequest();
        log.info("认证成功={}, {}", HttpUtil.getIpAddr(request), request.getHeader("User-Agent"));
        return passwordEncoder.encode(req.getPwd());
    }

    public String loginTip() {
        return pwdTips;
    }

}
